/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
  <file> uls_lex.c </file>
  <brief>
    Implements the main routines of ULS, such as uls_create(), 
      uls_gettok(), uls_destroy(), ...
    This file is part of ULS, Unified Lexical Scheme.
  </brief>
  <author>
    Stanley J. Hong <babel96@gmail.com>, April 2011.
  </author>
*/

#include "common.h"
#include "uls_lex.h"
#include "uls_conf.h"
#include "uls_num.h"
#include "istr_fd.h"
#include "uls_log.h"

static int __uls_is_real(const char *ptr);

// <brief>
// Sets the log-level of object for lexical analyzing.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="lvl">new value of log level</parm>
// <return>none</return>
void
uls_set_loglevel(uls_lex_t* uls, int lvl)
{
	if (lvl > ULS_MAX_LOGLEVEL || lvl < 0) {
		err_log("verbose level %d dosn't exist", lvl);
		return;
	}

	if (lvl < ULS_LOG_N_SYSTEMS) {
		if (lvl >= ULS_LOG_ERROR) {
			uls->loglevel &= ~((1<<ULS_LOG_N_SYSTEMS)-1); 
			uls->loglevel |= (1 << (lvl+1)) - 1;
		}
	} else {
		uls->loglevel |= (1 << lvl);
	}
}

// <brief>
// Checks if the 'lvl' is set in the internally.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="lvl">the log level to be checked.</parm>
// <return>true/false</return>
int
uls_loglevel_isset(uls_lex_t* uls, int lvl)
{
	if (lvl > ULS_MAX_LOGLEVEL || lvl < 0) {
		return 0;
	}

	return (uls->loglevel & ULS_LOGLEVEL_FLAG(lvl)) ? 1 : 0;
}

// <brief>
// Clears the log levels.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="lvl">the log level to be cleared.</parm>
// <return>none</return>
void
uls_clear_loglevel(uls_lex_t* uls, int lvl)
{
	if (lvl > ULS_MAX_LOGLEVEL || lvl < 0) {
		err_log("verbose level %d dosn't exist", lvl);
		return;
	}

	if (!uls_loglevel_isset(uls, lvl))
		return;

	if (lvl < ULS_LOG_N_SYSTEMS) {
		if (--lvl >= ULS_LOG_ERROR) {
			uls_set_loglevel(uls, lvl);
		}
	} else {
		uls->loglevel &= ~ULS_LOGLEVEL_FLAG(lvl);
	}
}

// <brief>
// The main method that creates an object for lexical analysis.
// </brief>
// <parm name="confname">The name of language spec or filepath</parm>
// <return>A lexical object</return>
uls_lex_t*
uls_create(const char* confname)
{
	uls_lex_t *uls;

	uls = uls_malloc(sizeof(uls_lex_t));

	if (uls_init(uls, confname) < 0) {
		uls_mfree(uls);
		return NULL;
	}

	return uls;
}

// <brief>
// This initializes the fields in the lexical object, 'uls'.
// </brief>
// <parm name="uls">The object for lexical analyzing</parm>
// <parm name="fp_ulc">The 'file-pointer' of ulc-file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
static int
uls_init_fp(uls_lex_t* uls, FILE* fp_ulc, FILE* fp_ulf)
{
	uls->loglevel = 0;
	uls_set_loglevel(uls, ULS_LOG_WARN);
	uls->syslog = (void *) stderr;
	uls->syslog_puts = uls_lf_puts_file;
	uls_lf_csz_init(&uls->mb_lbuf, NULL);

	uls_version_make(&uls->config_filever,
		ULC_VERSION_MAJOR, ULC_VERSION_MINOR,
		ULC_VERSION_DEBUG, ULC_VERSION_SEQUENCE);

	uls_version_make(&uls->spec_ver, 0, 0, 0, 0);

	uls_version_make(&uls->stream_filever,
		ULS_VERSION_STREAM_MAJOR, ULS_VERSION_STREAM_MINOR,
		ULS_VERSION_STREAM_DEBUG, ULS_VERSION_STREAM_SEQUENCE);

	uls->toknum_LINENUM = -3;
	uls->toknum_NONE = -2;
	uls->toknum_ERR = -1;
	uls->toknum_EOI = 0;
	uls->toknum_EOF = 1;
	uls->toknum_ID  = 2;
	uls->toknum_NUMBER = 3;

	uls_istr_init(&uls->i_str);

	str_init(&uls->tokbuf, &uls->tokbuf_siz, 2*(ULS_LEXSTR_MAXSIZ+1));
	// uls->tokbuf_siz == 64
	uls->tok = uls->toknum_NONE;

	return load_lex_conf(fp_ulc, fp_ulf, uls);
}

// <brief>
// This finds the file path of 'fname'
// The directory found of 'fname' is stored to 'p_dirpath' and its length to 'p_len_dirpath'.
// </brief>
// <parm name="dirpath_list">A directory list separated by ':' or ';'</parm>
// <parm name="fname">The wanted file for lexical configuration</parm>
// <parm name="p_dirpath">the directory of 'fname'</parm>
// <parm name="p_len_dirpath">the length of '*p_dirpath'</parm>
// <return>the file-descriptor of filepath found</return>
static int
uls_get_spec_fd(const char* dirpath_list, const char* fname, const char** p_dirpath, int* p_len_dirpath)
{
	char fpath_buff[ULS_FILEPATH_MAX+1];
	const char *fptr, *lptr0, *lptr;
	int   fdin, len, len_fptr;

	if (dirpath_list == NULL) {
		fdin = uls_open(fname, O_RDONLY);
		lptr = uls_filename(fname);
		if (p_dirpath != NULL) *p_dirpath = fname;
		if (p_len_dirpath != NULL) *p_len_dirpath = (int) (lptr - fname);
		return fdin;
	}

	for (fdin=-1, lptr0=dirpath_list; lptr0 != NULL; ) {
		if ((lptr = strchr(lptr0, ULS_DELIMCH_DIRPATH)) != NULL) {
			len_fptr = (int) (lptr - lptr0);
			fptr = lptr0;
			lptr0 = ++lptr;
		} else {
			len_fptr = strlen(lptr0);
			fptr = lptr0;
			lptr0 = NULL;
		}
		
		if (len_fptr==0) continue;

		strncpy(fpath_buff, fptr, len_fptr);
		len = len_fptr;
		fpath_buff[len++] = '/';
		uls_strcpy(fpath_buff+len, fname);

		if ((fdin = uls_open(fpath_buff, O_RDONLY)) >= 0) {
			if (p_dirpath != NULL) *p_dirpath = fptr;
			if (p_len_dirpath != NULL) *p_len_dirpath = len_fptr;
			break;
		}
	}

	return fdin;
}

// <brief>
// This makes an absolute path for 'spec_name' by combining it with 'dirpath'
// </brief>
// <parm name="dirpath">The directory of 'spec_name'</parm>
// <parm name="len_dirpath">The length of dirpath</parm>
// <parm name="pathbuff">The output buffer for the absolute path of 'spec_name'</parm>
// <return>The length of 'pathbuff'</return>
static int
get_ulf_filepath(const char* dirpath, int len_dirpath, char *spec_name, char* pathbuff)
{
	int len;

	if (len_dirpath > 0) {
		memcpy(pathbuff, dirpath, len_dirpath);
		len = len_dirpath;
		pathbuff[len++] = '/';
	} else {
		len = 0;
	}

	len += uls_sprintf(pathbuff + len, "%s.ulf", spec_name);
	return len;
}

// <brief>
// Initializes lexical object with lexical configuration.
// Mainly called by uls_create()
// </brief>
// <parm name="uls">The object for lexical analyzing</parm>
// <parm name="confname">The name of language spec or its filepath</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_init(uls_lex_t* uls, const char* confname)
{
	char fname_buff[ULS_LEXSTR_MAXSIZ+5];
	const char *pathlist, *dirpath, *searchpath[8];
	int  i, rc, n=0, len, fdin, len_dirpath;
	const char *ptr, *fname;

	char ulf_filepath[ULS_FILEPATH_MAX+1];
	FILE  *fp_ulc, *fp_ulf;

	memset(uls, 0x00, sizeof(uls_lex_t));

	if ((ptr=uls_strchr_r(confname, '.'))!=NULL && uls_streql(ptr+1, "ulc")) {
		// confname: filepath
		if ((len=uls_get_specname(confname, uls->spec_name))<0) {
			err_log("%s: Invalid name for spec-name", confname);
			return -1;
		}

		fname = confname;
		searchpath[n++] = NULL;

	} else {
		// confname: spec-name
		if ((len=uls_is_valid_specname(confname)) <= 0) {
			err_log("%s: invalid spec-name", confname);
			return -1;
		}
		uls_strcpy(uls->spec_name, confname);

		uls_strcpy(fname_buff, confname);
		uls_strcpy(fname_buff+len, ".ulc");
		fname = fname_buff;
	}

	if ((pathlist=getenv("WHERE_ULC_ARE")) != NULL) {
		searchpath[n++] = pathlist;
	}

	searchpath[n++] = ULS_CONFIG_DIR;
	searchpath[n++] = ULS_CONFIG_DFLDIR;

	for (fp_ulf=NULL, i=0; ; i++) {
		if (i >= n) {
			err_log("can't find ulc file for '%s'.", confname);
			uls_list_ulc_searchpaths(confname);
			return -1;
		}

		if ((fdin=uls_get_spec_fd(searchpath[i], fname, &dirpath, &len_dirpath)) >= 0) {
			get_ulf_filepath(dirpath, len_dirpath, uls->spec_name, ulf_filepath);
			fp_ulf = fopen(ulf_filepath, "r");
			break;
		}
	}

	if ((fp_ulc=uls_fdopen(fdin, "r")) == NULL) {
		err_log("fail to open '%s'!", confname);
		if (fp_ulf != NULL) fclose(fp_ulf);
		return -1;
	}

	rc = uls_init_fp(uls, fp_ulc, fp_ulf);

	fclose(fp_ulc); // fdin closed!
	fdin = -1;
	if (fp_ulf != NULL) fclose(fp_ulf);

	uls->tokbuf[0] = '\0';
	uls->s_val = uls->tokbuf;
	uls->s_val_len = 0;
	uls->tok = uls->toknum_EOI;

	return rc;
}

// <brief>
// The counterpart of 'uls_init()'.
// de-allocates all the memories in 'uls'
// </brief>
// <parm name="uls">The object for lexical analyzing</parm>
// <return>none</return>
void
uls_deinit(uls_lex_t* uls)
{
	istr_context_t *istr_ctx, *istr_ctx_prev;

	str_free(&uls->tokbuf, &uls->tokbuf_siz);
	uls->tok = -1;
	uls->n_digits = uls->expo = 0;

	for (istr_ctx=uls->istr_tower; istr_ctx!=NULL; istr_ctx=istr_ctx_prev) {
		istr_ctx_prev = istr_ctx->prev;
		istr_context_destroy(istr_ctx);
	}

	uls->istr_tower = NULL;

	uls->i_str.fd_context.fill_proc = default_fillproc;
	uls_istr_reset(&uls->i_str);

	uls_deinit_kwtable(&uls->lxm_keyw_table);
	uls->lxm_tokid_sorted = uls_mfree(uls->lxm_tokid_sorted);
	free_lxm_array(uls);

	uls_istr_deinit(&uls->i_str);
	uls_lf_csz_deinit(&uls->mb_lbuf);
}

// <brief>
// The counterpart of 'uls_create()'.
// de-allocates all the memories in 'uls'
// </brief>
// <parm name="uls">The object for lexical analyzing</parm>
// <return>none</return>
void
uls_destroy(uls_lex_t* uls)
{
	if (uls == NULL)
		return;

	uls_deinit(uls);
	uls_mfree(uls);
}

// <brief>
// Changes the keyword compare-interface 'uls'.
// </brief>
// <parm name="proc">A new interface.</parm>
// <return>none</return>
void
uls_change_strcmp(uls_lex_t* uls, uls_strcmp_t proc)
{
	if (proc==NULL) proc = lexi_order_ascii;
	uls->lxm_strcmp = proc;
}

// <brief>
// Returns the keyword(c-string) corresponding to the token 't'.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="t">The token id for which we want know its keyword string.</parm>
// <return>keyword string</return>
uls_char_t* uls_get_keyword(uls_lex_t* uls, int t)
{
	uls_char_t *keyw;

	if ((keyw=get_keyword_str(uls, t)) == NULL)
		return (uls_char_t *) "?";
		
	return keyw;
}

// <brief>
// If the current token-id is not 'value', An exception will be thrown.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="value">I expect that the token be same as 'value'</parm>
// <return></return>
void
uls_expect(uls_lex_t* uls, int value)
{
	int val0 = uls->tok;

	if (val0 != value) {
		if (isprint(val0))
			uls_log(uls, "tok-'%c'", val0);
		else
			uls_log(uls, "tok-%d", val0);

		if (isprint(value))
			uls_panic(uls, "tok-'%c' expected!!", value);
		else
			uls_panic(uls, "tok-%d expected!!", value);
	}
}

// <brief>
// Refill the lexical object 'uls' with input data 
//    resulted from the input-source.
// The 'input-source' may be file or literal string.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>0 if it succeeds, -1 otherwise</return>
static int
uls_fillbuff(uls_lex_t* uls)
{
	uls_istream_t* istr = &uls->i_str;
	uls_char_t *line;
	int rc;

	if (istr->fd_context.lptr < istr->fd_context.line_end) {
		return 0;
	}

	if (istr->fd_context.fd_source.raw_buf_bytes <= 0) {
		if (istr->fd_context.fd_source.flags & ISTR_FD_EOF) {
			if (istr->fd_context.fd_source.flags & ISTR_FD_ERR) {
				err_log("%s: called again after I/O failed!", __FUNCTION__);
				return -1;
			}
			return 0;
		}
	}

	csz_reset(&uls->i_str.fd_context.fd_source.ubuf);
	rc = istr->fd_context.fill_proc(istr, &line, istr->fd_context.literals, &istr->fd_context.n_literals);
	if (rc < 0 || (rc==0 && istr->fd_context.n_literals==0)) {
		istr->fd_context.lptr = istr->fd_context.line = (uls_char_t *) "";
		istr->fd_context.line_end = istr->fd_context.line;
		return rc;
	}

	istr->fd_context.i_literals = 0;

	istr->fd_context.lptr = istr->fd_context.line = line;
	if ((istr->fd_context.line_end=line+rc) < line) {
		err_log("%s: invalid string length, %d.", __FUNCTION__, rc);
		return -1;
	}

	uls->tok = uls->toknum_NONE;
	uls->tokbuf[0] = '\0';
	uls->s_val = uls->tokbuf;
	uls->s_val_len = 0;

	return 1;
}

// <brief>
// Opens the 'filepath' with its information 'hdr'
// The file specified by 'filepath' may be (raw) source code or uls-file.
// </brief>
// <parm name="filepath">The path of file to be opened</parm>
// <parm name="hdr">The file header having information on 'filepath'</parm>
// <return>The file descriptor of 'filepath'</return>
int
uls_open_stream_file(const char* filepath, uls_stream_t* hdr)
{
	int fd;

	if ((fd=uls_open(filepath, O_RDONLY | _O_BINARY)) < 0) {
		err_log("%s: Not found", filepath);
		return -1;
	}

	if (uls_open_stream_fd(fd, hdr) < 0) {
		err_log("%s: can't conjecture file type!", filepath);
		uls_close(fd);
		return -1;
	}

	hdr->flags |= ULS_STREAM_FDCLOSE;
	strncpy(hdr->tag, filepath, ULS_FILEPATH_MAX);
	hdr->tag[ULS_FILEPATH_MAX] = '\0';

	return fd;
}

// <brief>
// Pushes the open file 'fd' to the tower of input-sources
// Makes the lexical object to be ready to tokenize.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">The file descriptor for input-source</parm>
// <parm name="want_eof">Want to receive the tokens, end-of-file, TOK_EOF</parm>
// <return>0 if it succeeds, -1 otherwise</return>
static int
uls_push_fd_no_fill(uls_lex_t* uls, int fd, int want_eof)
{
	istr_context_t *istr_ctx;

	if (fd < 0) {
		err_log("%s: NOT PERMITTED, fd < 0", __FUNCTION__);
		return -1;
	}

	if (uls->istr_tower == NULL) {
		uls->i_str.fd_context.last_end_mark = NULL;
	}

	istr_ctx = export_istr_context(&uls->i_str);
	istr_ctx->prev = uls->istr_tower;
	uls->istr_tower = istr_ctx;

	istr_context_init(&uls->i_str.fd_context, fd);
	if (want_eof)
		uls->i_str.fd_context.flags |= ULS_WANT_EOF_TOK;

	return 0;
}

// <brief>
// Pushes the open file 'fd' to the tower of input-sources
// Makes the lexical object to be ready to tokenize.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">The file descriptor for input-source</parm>
// <parm name="fd_type"></parm>
// <parm name="flags">ULS_WANT_EOF_TOK</parm>
// <return>0 if it succeeds, -1 otherwise</return>
static int
uls_push_fd_type_nofill(uls_lex_t* uls, int fd, int fd_type, int flags)
{
	istr_fd_t* fd_src;
	uls_gettok_t  proc;
	istr_filler_t fill_proc;
	istr_fd_checker_t fill_subproc;

	if ((proc=find_fd_filler(fd_type, uls->i_str.n_commtypes,
		&fill_proc, &fill_subproc)) == NULL) {
		return -1;
	}

	if (uls_push_fd_no_fill(uls, fd, flags & ULS_WANT_EOF_TOK) < 0) {
		return -1;
	}

	fd_src = &uls->i_str.fd_context.fd_source;
	fd_src->record_boundary_checker = fill_subproc;

	uls->i_str.fd_context.gettok = proc;
	uls->i_str.fd_context.fill_proc = fill_proc;

	return 0;
}

// <brief>
// This method pushes (raw) source-file on the internal stack.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="fd_type">ULS_STREAM_RAW | ULS_STREAM_BIN_LE | ULS_STREAM_BIN_BE</parm>
// <parm name="tag">An information on 'fd'</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_push_fd_type(uls_lex_t* uls, int fd, int fd_type, const char* tag, int flags)
{
	if (uls_push_fd_type_nofill(uls, fd, fd_type, flags) < 0) {
		return -1;
	}

	uls_istr_set_tag(&uls->i_str, tag, 1);

	if (uls_fillbuff(uls) < 0) {
		err_log("%s: IO-error", __FUNCTION__);
		return -1;
	}

	return 0;
}

// <brief>
// This method does the same things as pushFdType() except this accepts the parameter hdr, the header info of uls-file.
// There are the attributes that pushFdType() needs to know.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="hdr">the header information of uls-file(*.uls)</parm>
// <parm name="fd_type">ULS_STREAM_RAW | ULS_STREAM_BIN_LE | ULS_STREAM_BIN_BE</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_push_fd_type_hdr(uls_lex_t* uls, uls_stream_t* hdr, int flags)
{
	if (uls_push_fd_type_nofill(uls, hdr->fd, hdr->filetype, flags) < 0) {
		return -1;
	}

	uls_istr_set_tag(&uls->i_str, hdr->tag, 1);

	init_line_in_fd_src(&uls->i_str.fd_context.fd_source, hdr->firstline, hdr->len_firstline);
	if (uls_fillbuff(uls) < 0) {
		err_log("%s: IO-error", __FUNCTION__);
		return -1;
	}

	return 0;
}

// <brief>
// This method will push the input-file with its header on the internal stack.
// The 'hdr' may be of RAW or of ULS(*.uls).
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="hdr">the header information of uls-file(*.uls)</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_push_file_hdr(uls_lex_t* uls, uls_stream_t* hdr, int flags)
{
	if (uls_push_fd_type_nofill(uls, hdr->fd, hdr->filetype, flags) < 0) {
		return -1;
	}

	uls_istr_set_tag(&uls->i_str, hdr->tag, 1);

	init_line_in_fd_src(&uls->i_str.fd_context.fd_source, hdr->firstline, hdr->len_firstline);
	if (uls_fillbuff(uls) < 0) {
		err_log("%s: IO-error", __FUNCTION__);
		return -1;
	}

	return 0;
}

// <brief>
// This method will push the input-file with its file path on the internal stack of input-sources.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="filepath">The file path of input.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_push_file(uls_lex_t* uls, const char* filepath, int flags)
{
	uls_stream_t hdr;
	int fd, stat=0;

	if ((fd=uls_open_stream_file(filepath, &hdr)) < 0) {
		err_log("fail to file header");
		return -1;
	}

	if (hdr.filetype != ULS_STREAM_RAW) {
		if (!uls_streql(uls->spec_name, hdr.specname) ||
			uls_version_cmp_code(&uls->stream_filever, &hdr.filever) < 2) {
			err_log("%s: Unsupported version: ", filepath);
			uls_close_stream(&hdr);
			return -1;
		}
	}

	if (uls_push_file_hdr(uls, &hdr, flags) < 0) {
		stat = -1;
	}

	hdr.flags &= ~ULS_STREAM_FDCLOSE;
	uls_close_stream(&hdr);

	return stat;
}

// <brief>
// The counterpart of uls_push_fd()
// You needn't call this as it's automatically called by ULS.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>The current file descriptor used</return>
int
uls_pop_fd(uls_lex_t* uls)
{
	istr_context_t *istr_ctx;
	int fd_ret, must_close;

	fd_ret = uls->i_str.fd_context.fd_source.fd;
	must_close = uls->i_str.fd_context.fd_source.flags & ISTR_FD_AUTOCLOSE;

	if ((istr_ctx=uls->istr_tower) != NULL) {
		uls->istr_tower = istr_ctx->prev; // detached
		import_istr_context(istr_ctx, &uls->i_str);
	} else {
		istr_context_init(&uls->i_str.fd_context, -1);
	}

	if (fd_ret >= 0 && must_close) {
		uls_close(fd_ret);
		fd_ret = -1;
	}

	return fd_ret; // the fd of the previous fd-source
}

// <brief>
// This sets the buffer in the lexical object with literal string 'line'.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="line">The literal string to be set</parm>
// <parm name="line">The length of 'line'</parm>
// <parm name="do_push">checks whether the current context of input-source is dismissed or not.</parm>
// <return>none</return>
static void
uls_set_line_internal(uls_lex_t* uls, uls_char_t* line, int line_len, int do_push)
{
	istr_context_t *istr_ctx = &uls->i_str.fd_context;
	istr_context_t *fd_ctx;
	istr_fd_t *fd_src;

	if (line == NULL) {
		line = (uls_char_t*) "";
		line_len = 0;
	} else if (line_len < 0) {
		line_len = uls_strlen(line);
	}

	if (do_push || uls->istr_tower == NULL) {
		if (uls->istr_tower == NULL) {
			uls->i_str.fd_context.last_end_mark = NULL;
		}
		fd_ctx = export_istr_context(&uls->i_str);
		fd_ctx->prev = uls->istr_tower;
		uls->istr_tower = fd_ctx;
		istr_context_init(istr_ctx, -1);
	} else {
		istr_context_reset(istr_ctx, -1);
	}

	istr_ctx->gettok = uls_gettok_raw;

	fd_src = &istr_ctx->fd_source;
	fd_src->raw_bufptr = line;
	fd_src->raw_buf_bytes = line_len;

	uls_istr_set_line(&uls->i_str, NULL, 0);
	istr_ctx->fill_proc = istr_mem_filler;

	uls->tok = uls->toknum_NONE;
	uls->tokbuf[0] = '\0';
	uls->s_val = uls->tokbuf;
	uls->s_val_len = 0;
}

// <brief>
// This method will overwrite the input-source on the top of the stack into 'line'.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="line">A input source as an literal string</parm>
// <parm name="len">The length of 'line'</parm>
// <parm name="flags">You can set the flag ULS_WANT_EOF_TOK.</parm>
// <return>none</return>
void
uls_set_line(uls_lex_t* uls, char* line, int len, int flags)
{
	uls_set_line_internal(uls, (uls_char_t *) line, len, 0);

	if (flags & ULS_WANT_EOF_TOK) 
		uls->i_str.fd_context.flags |= ULS_WANT_EOF_TOK;
}

// <brief>
// This method will push the literal string as an input-source on the top of the stack.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="line">A input source as an literal string</parm>
// <parm name="len">The length of 'line'</parm>
// <parm name="flags">You can set the flag ULS_WANT_EOF_TOK.</parm>
// <return>none</return>
void
uls_push_line(uls_lex_t* uls, char* line, int len, int flags)
{
	uls_set_line_internal(uls, (uls_char_t *) line, len, 1);

	if (flags & ULS_WANT_EOF_TOK) 
		uls->i_str.fd_context.flags |= ULS_WANT_EOF_TOK;
}

// <brief>
// Sets the input file to be tokenized by giving its file descriptor 'fd'
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="fd_type">The type of 'fd'. The type of input file can be source(RAW) or ULS-file(*.uls).</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
static int
uls_set_fd_type_nofill(uls_lex_t* uls, int fd, int fd_type, int flags)
{
	if (uls->istr_tower != NULL) {
		uls_pop_fd(uls);
	}

	if (uls_push_fd_type_nofill(uls, fd, fd_type, flags) < 0) {
		return -1;
	}

	return 0;
}

// <brief>
// This overwrites or calls off the state of the current input-source and
// sets the new file of 'fd' to the input-source to be processed.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">the file descriptor of input file.</parm>
// <parm name="tag">the string on 'fd'.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_set_fd_type(uls_lex_t* uls, int fd, int fd_type, const char* tag, int flags)
{
	if (uls_set_fd_type_nofill(uls, fd, fd_type, flags) < 0) {
		return -1;
	}

	uls_istr_set_tag(&uls->i_str, tag, 1);

	if (uls_fillbuff(uls) < 0) {
		err_log("%s: IO-error", __FUNCTION__);
		return -1;
	}

	return 0;
}

// <brief>
// This method will overwrite the current input-source on the top of stack to new one with its header.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="hdr">the header information of uls-file(*.uls)</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_set_file_hdr(uls_lex_t* uls, uls_stream_t* hdr, int flags)
{
	if (uls_set_fd_type_nofill(uls,  hdr->fd, hdr->filetype, flags) < 0) {
		return -1;
	}

	uls_istr_set_tag(&uls->i_str, hdr->tag, 1);

	init_line_in_fd_src(&uls->i_str.fd_context.fd_source, hdr->firstline, hdr->len_firstline);
	if (uls_fillbuff(uls) < 0) {
		err_log("%s: IO-error", __FUNCTION__);
		return -1;
	}

	uls->i_str.fd_context.fd_source.flags |= ISTR_FD_AUTOCLOSE;
	return 0;
}

// <brief>
// This method will overwrite the current input-source on the top of stack to new one with its file path.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="filepath">The file path of input.</parm>
// <parm name="flags">Set ULS_WANT_EOF_TOK if you want TOK_EOF whenever each file reaches at the end of file</parm>
// <return>none</return>
int
uls_set_file(uls_lex_t* uls, const char* filepath, int flags)
{
	uls_stream_t hdr;
	int fd, stat = 0;

	if ((fd=uls_open_stream_file(filepath, &hdr)) < 0) {
		err_log("fail to file header");
		return -1;
	}

	if (hdr.filetype != ULS_STREAM_RAW) {
		if (!uls_streql(uls->spec_name, hdr.specname) ||
			uls_version_cmp_code(&uls->stream_filever, &hdr.filever) < 2) {
			err_log("%s: Unsupported version: ", filepath);
			uls_close_stream(&hdr);
			return -1;
		}
	}

	if (uls_set_file_hdr(uls, &hdr, flags) < 0)
		stat = -1;

	hdr.flags &= ~ULS_STREAM_FDCLOSE;
	uls_close_stream(&hdr);

	return stat;
}

// <brief>
// Writes the number string to 'tokbuf' in 'uls'
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="numflags">ULS_NUM_FL_ZERO | ULS_NUM_FL_MINUS</parm>
// <parm name="ptr">the sequence of digits to be printed</parm>
// <parm name="n2">the # of digits in 'ptr'</parm>
// <return>the # of bytes written in 'uls->tokbuf'</return>
static int
write_lexnum_to_tokbuf(uls_lex_t* uls,
	int numflags, uls_char_t *ptr, int n2)
{
	uls_char_t     **p_outbuf = &uls->tokbuf, *ptr2;
	int      *p_outbuf_siz = &uls->tokbuf_siz;
	int      j, k = 0, n1=uls->n_digits;

	if (numflags & ULS_NUM_FL_ZERO) {
		if (numflags & ULS_NUM_FL_FLOAT) {
			str_putc(p_outbuf, p_outbuf_siz, k++, '.');
			str_putc(p_outbuf, p_outbuf_siz, k++, '0');
		} else {
			str_putc(p_outbuf, p_outbuf_siz, k++, '0');
		}

		str_putc(p_outbuf, p_outbuf_siz, k, '\0');
		return k;
	}

	if (numflags & ULS_NUM_FL_MINUS)
		str_putc(p_outbuf, p_outbuf_siz, k++, '-');

	if (numflags & ULS_NUM_FL_HEXA) {
		for (j=0; j<n1; j++) {
			str_putc(p_outbuf, p_outbuf_siz, k++, toupper(ptr[j]));
		}
		ptr += n1;
		str_putc(p_outbuf, p_outbuf_siz, k, '\0');

	} else if (numflags & ULS_NUM_FL_FLOAT) {
		str_putc(p_outbuf, p_outbuf_siz, k++, '.');
		if (n1 > 0) {
			k = str_append(p_outbuf, p_outbuf_siz, k, ptr, n1);
			ptr += n1;
			uls->expo += n1;
		}

		if (*ptr == '.') ++ptr;

		/* print the digits below '.' */
		k = str_append(p_outbuf, p_outbuf_siz, k, ptr, n2);

		if (uls->expo != 0) {
			// alloc memory for the exponent.
			str_append(p_outbuf, p_outbuf_siz, k, NULL, 32);
			// NOTICE: 32 digits are enough for the -INTMAX
			ptr2 = *p_outbuf + k;
			k += uls_snprintf((uls_lf_char_t *) ptr2, *p_outbuf_siz - k, "E%d", uls->expo);
		}

		uls->n_digits += n2;
		str_putc(p_outbuf, p_outbuf_siz, k, '\0');

	} else {
		str_append(p_outbuf, p_outbuf_siz, k, NULL, n1 + 1 + n1);
		ptr2 = *p_outbuf + k + n1 + 1;
		memcpy(ptr2, ptr, n1);
		ptr2[n1] = '\0';
		k += uls_dec2hex_str(ptr2, *p_outbuf+k);
	}

	return k;
}

// <brief>
// Recognizes number string as token(integer or real)  
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="p_lptr">The pointer in the internal buffer</parm>
// <return>The length of the number string</return>
static int
get_number(uls_lex_t* uls, uls_char_t** p_lptr)
{
	uls_char_t* numptr, *lptr;
	int numflags, n2=0, len, l_cnst_suffix;
	char *cnst_suffixes, *cnst_suffix;
	char ch;

	uls->expo = 0;

	if ((numptr=num2stdfmt_0(p_lptr,
		&numflags, &uls->n_digits, &uls->tokbuf, &uls->tokbuf_siz))==NULL) {
		if ((numflags & ULS_NUM_FL_ERROR) ||
			(numptr = num2stdfmt(p_lptr,
				&numflags, &uls->n_digits, &n2, &uls->expo))==NULL ) {
			return -1; // --> not number
		}
	}

	if (numflags & ULS_NUM_FL_TOKBUF_USED) {
		len = uls_strlen(uls->tokbuf);
	} else {
		len = write_lexnum_to_tokbuf(uls, numflags, numptr, n2);
	}

	uls->s_val = uls->tokbuf;

	if ((ch=*(lptr=*p_lptr)) != '\0' && !isspace(ch)) {
		if (__uls_is_real((const char *) uls->s_val)) {
			cnst_suffixes = uls->fcnst_suffixes;
		} else {
			cnst_suffixes = uls->icnst_suffixes;
		}

		if ((cnst_suffix=is_cnst_suffix_contained(cnst_suffixes, (char *) lptr, -1, NULL)) != NULL) {
			l_cnst_suffix = strlen(cnst_suffix);
			str_putc(&uls->tokbuf, &uls->tokbuf_siz, len++, ' ');
			len = str_append(&uls->tokbuf, &uls->tokbuf_siz, len, (uls_char_t *) cnst_suffix, l_cnst_suffix);
			str_putc(&uls->tokbuf, &uls->tokbuf_siz, len, '\0');
			*p_lptr = lptr += l_cnst_suffix;
		}
	}

	return len;
}

// <brief>
// Skips the white chars irregardless of token.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>the pointer of buffer</return>
uls_char_t*
uls_skip_white_spaces(uls_lex_t* uls)
{
	uls_istream_t* istr = &uls->i_str;
	uls_char_t* ch_ctx = istr->ch_context;
	uls_char_t  *lptr = istr->fd_context.lptr;
	uls_char_t ch;
	int  rc;

	for ( ; (ch=*lptr) != '\0'; lptr+=rc) {
		if ((rc=uls_is_char_idfirst(uls, lptr)) > 0)
			break;

		if ((rc = -rc) == 1) {
			if (ch=='\n')
				++istr->fd_context.lineno;
	
			if (ch_ctx[ch] != 0)
				break;
		}
	}

	istr->fd_context.lptr = lptr;
	return lptr;
}

// <brief>
// Peeks the next char in the internal buffer.
// Returns the corresponding information with the literal string in case that the next char is a quotation char.
// </brief>
// <parm name="pp_quotetype">out-param, The information of the literal string</parm>
// <return>The char which this object indicates to as the next char.</return>
uls_char_t
uls_peekch_detail(uls_lex_t* uls, istr_quotetype_t** pp_quotetype)
{
	uls_istream_t *istr = &uls->i_str;
	uls_char_t     *ch_ctx = istr->ch_context;
	uls_char_t     *lptr, ch, ch_grp;
	int      k, rc, stat;

	if (pp_quotetype != NULL) 
		*pp_quotetype = NULL;

again_1:
	lptr = istr->fd_context.lptr;

	if (*lptr == '\0') {
		k = istr->fd_context.i_literals;
		if (k >= istr->fd_context.n_literals) {
			if ((rc=uls_fillbuff(uls)) < 0) {
				err_panic("%s: IO-error", __FUNCTION__);
			} else if (rc==0) {
				return ULS_NEXTCH_NONE;
			}
			goto again_1;
		} else {
			istr_quotetype_t *q;
			q = find_quotetype_by_tokid(
				istr->quotetypes, istr->n_quotetypes, istr->fd_context.literals[k].tok_id);
			if (q == NULL) {
				err_panic("No matching quote-string for tok-id(%d) found",
					istr->fd_context.literals[k].tok_id);
			}

			if (pp_quotetype != NULL) 
				*pp_quotetype = q;

			return q->start_mark[0];
		}
	}

	stat = ch = *lptr;

	if ((ch_grp=ch_ctx[ch=*lptr]) == 0) {
		if (ch!='\t' && ch!='\n') stat = ' ';
	}

	return stat;
}

// <brief>
// Extracts the next character.
// </brief>
// <return>The next character</return>
uls_char_t
uls_getch(uls_lex_t* uls)
{
	istr_quotetype_t *q;
	uls_char_t    ch;

	if (uls->i_str.fd_context.tok_tower_len> 0 && uls_loglevel_isset(uls, ULS_LOG_ERROR))
		err_log("While %s(), there are pushed-tokens in the current input stream", __FUNCTION__);

	if ((ch=uls_peekch_detail(uls, &q)) == ULS_NEXTCH_NONE)
		return ULS_NEXTCH_NONE;

	if (q != NULL) return ULS_NEXTCH_QUOTE;

	++uls->i_str.fd_context.lptr;

	return ch;
}

// <brief>
// Cancel the call of 'uls_getch()'
// </brief>
// <return>none</return>
int
uls_ungetch(uls_lex_t* uls)
{
	if (uls->i_str.fd_context.tok_tower_len> 0 && uls_loglevel_isset(uls, ULS_LOG_ERROR))
		err_log("While %s(), there are pushed-tokens in the current input stream", __FUNCTION__);

	if (uls->i_str.fd_context.line < uls->i_str.fd_context.lptr) {
		--uls->i_str.fd_context.lptr;
		return 1;
	}

	return 0;
}

static int
append_open_comment_to_lexeme(uls_lex_t* uls, int k, istr_commtype_t *cmt)
{
	if (cmt != NULL) {
		if (k > 0) str_putc(&uls->tokbuf, &uls->tokbuf_siz, k++, ' ');
		k = str_append(&uls->tokbuf, &uls->tokbuf_siz, k,
			cmt->end_mark, cmt->len_end_mark);
	}

	str_putc(&uls->tokbuf, &uls->tokbuf_siz, k, '\0');

	uls->s_val = uls->tokbuf;
	uls->s_val_len = k;

	return k;
}

// <brief>
// This tokenizes the (raw) source files or literal strings.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>0 if it succeeds, 1 if not arranged</return>
int uls_gettok_raw(uls_lex_t* uls)
{
	uls_istream_t *istr = &uls->i_str;
	uls_char_t* ch_ctx = istr->ch_context;

	uls_char_t     *lptr;
	uls_char_t           ch, ch_grp;
	uls_literal_t  *lit;
	int            j, k, rc0, rc;
	uls_tokdef_t  *e;

 next_loop:
	lptr = uls_skip_white_spaces(uls);

	ch_grp = ch_ctx[ch=*lptr];

	if ((ch_grp & ULS_CH_DIGIT) && (k=get_number(uls, &lptr)) > 0) {
		uls->tok = uls->toknum_NUMBER;
		uls->s_val = uls->tokbuf;
		uls->s_val_len = k;

	} else if ((rc0=uls_is_char_idfirst(uls, lptr)) > 0) {
		k = 0;
		rc = rc0;
		do {
			for (j=0; j<rc; j++) {
				str_putc(&uls->tokbuf, &uls->tokbuf_siz, k++, *lptr++);
			}
		} while ((rc=uls_is_char_id(uls, lptr)) > 0);

		str_putc(&uls->tokbuf, &uls->tokbuf_siz, k, '\0');

		uls->s_val = uls->tokbuf;
		uls->s_val_len = k;

		if ((e=is_keyword_idstr(uls, uls->tokbuf, k)) != NULL) {
			uls->tok = e->tok_id;
		} else {
			uls->tok = uls->toknum_ID;
		}

	} else if ((rc0 < 0 || (ch_grp & ULS_CH_2PLUS)) &&
			(e=is_keyword_intrinsic(uls,lptr,-1))!=NULL) {
		/* found */
		uls_strcpy(uls->tokbuf, e->keyword);
		uls->s_val = uls->tokbuf;
		uls->s_val_len = e->l_keyword;
		lptr += e->l_keyword;
		uls->tok = e->tok_id;

	} else if (ch_grp & ULS_CH_1) {
		uls->tokbuf[0] = *lptr++;
		uls->tokbuf[1] = '\0';

		if (ch_grp & ULS_CH_TOKID_REMAPPED) {
			e = is_keyword_1char(uls, uls->tokbuf[0]);
			uls_assert(e!=NULL);
			uls->tok = e->tok_id;
		} else {
			uls->tok = uls->tokbuf[0];
		}

		uls->s_val = uls->tokbuf;
		uls->s_val_len = 1;

	} else if (ch == '\0') {
		k = istr->fd_context.i_literals;

		if (k >= istr->fd_context.n_literals) {
			if ((rc=uls_fillbuff(uls)) < 0) {
				err_log("%s: IO-error", __FUNCTION__);
				uls->tok = uls->toknum_ERR;
				uls->tokbuf[0] = '\0';
				uls->s_val = uls->tokbuf;
				uls->s_val_len = 0;
				return 0;
			} else if (rc > 0) {
				// actually, rc == 1
				goto next_loop; // normal case
			}

			if (uls->istr_tower == NULL) {
				uls->tok = uls->toknum_EOI;
				append_open_comment_to_lexeme(uls, 0, istr->fd_context.last_end_mark);
				return 0;
			}

			uls->tok = uls->toknum_NONE;
			return 1;
		}

		// QUOTE-STRING
		lit = istr->fd_context.literals + k;
		istr->fd_context.i_literals = ++k;

		if (lit->text == NULL) {
			uls->s_val = istr->fd_context.fd_source.ubuf.pool + lit->offset2;
		} else {
			uls->s_val = lit->text;
		}

		uls->s_val_len = lit->len_text;
		uls->tok = lit->tok_id;

		istr->fd_context.lineno += lit->n_lfs;
		lptr = istr->fd_context.fd_source.ubuf.pool + lit[1].offset1;

	} else {
		++lptr;
		goto next_loop;
	}

	istr->fd_context.lptr = lptr;

	return 0;
}

// <brief>
// Advances the cursor of the lexical object to the next step.
// Returns an integer representing token id.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>the token id</return>
int uls_gettok(uls_lex_t* uls)
{
	istr_context_t *istr_ctx;
	istr_commtype_t *cmt_lost;
	uls_token_t  *lit;
	int          k, lno, rc, fd_prev;
	char         *tagstr;

	istr_ctx = &uls->i_str.fd_context;

	if (uls->tok == uls->toknum_EOI) {
		append_open_comment_to_lexeme(uls, 0, istr_ctx->last_end_mark);
		return uls->toknum_EOI;
	}

	if (istr_ctx->tok_tower_len > 0) {
		lit = istr_ctx->tok_tower + istr_ctx->tok_tower_len - 1;

		uls->tok =  lit->id;

		if (lit->offset >= 0) {
			uls->s_val = istr_ctx->cstr_pool + lit->offset;
			uls->s_val_len = lit->len_text;
			rc = str_append(&uls->tokbuf, &uls->tokbuf_siz, 0, uls->s_val, uls->s_val_len);
			str_putc(&uls->tokbuf, &uls->tokbuf_siz, rc, '\0');
			uls->s_val = uls->tokbuf;

			istr_ctx->cstr_pool_len = lit->offset;

		} else {
			uls->s_val = lit->text;
			uls->s_val_len = lit->len_text;
		}

		--istr_ctx->tok_tower_len;
		return uls->tok;
	}

	while (istr_ctx->gettok(uls) != 0) {
		cmt_lost = istr_ctx->last_end_mark;

		if ((istr_ctx->flags & ULS_WANT_EOF_TOK) && uls->tok != uls->toknum_EOF) {
			uls->tok = uls->toknum_EOF;

			lno = uls_get_lineno(uls);
			tagstr = (char *) uls->i_str.fd_context.tag;
			uls->i_str.fd_context.tag = NULL;
			uls->i_str.fd_context.tag_siz = 0;
			k = strlen(tagstr) + 128;

			fd_prev = uls_pop_fd(uls);

			str_append(&uls->tokbuf, &uls->tokbuf_siz, 0, NULL, k);
			k = uls_snprintf((uls_lf_char_t*)uls->tokbuf, k, "%d %d %s", fd_prev, lno, tagstr);
			k = append_open_comment_to_lexeme(uls, k, cmt_lost);
			uls_mfree(tagstr);

			if (uls->istr_tower == NULL)
				uls->i_str.fd_context.last_end_mark = cmt_lost;

			return uls->toknum_EOF;
		}

		fd_prev = uls_pop_fd(uls);

		if (uls->istr_tower == NULL)
			uls->i_str.fd_context.last_end_mark = cmt_lost;
	}

	return uls->tok;
}

// <brief>
// This set forcibly the token of the lexical object.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="tokid">A token number</parm>
// <parm name="lexeme">The lexeme associated with 'sym_no'</parm>
// <parm name="l_lexeme">The length of 'lexeme'</parm>
// <return>none</return>
void uls_settok(uls_lex_t* uls, int tokid, char* lexeme, int l_lexeme)
{
	if (l_lexeme < 0) {
		uls->s_val = (uls_char_t *) lexeme; 
	} else {
		str_append(&uls->tokbuf, &uls->tokbuf_siz, 0, (uls_char_t *) lexeme, l_lexeme);
		str_putc(&uls->tokbuf, &uls->tokbuf_siz, l_lexeme, '\0');
		uls->s_val = uls->tokbuf;
	}

	uls->s_val_len = l_lexeme;
	uls->tok = tokid;
}

// <brief>
// An arbitrary token 'sym_no' will be the next token after this method is called.
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="sym_no">A token number</parm>
// <parm name="dat">The lexeme associated with 'sym_no'</parm>
// <parm name="dat_len">The length of 'dat'</parm>
// <return>none</return>
void uls_pushback(uls_lex_t* uls, int sym_no, void* dat, int dat_len)
{
	istr_context_t *istr_ctx = &uls->i_str.fd_context;
	uls_token_t  *bak_lit;
	int k, n;

	if (istr_ctx->tok_tower_len >= istr_ctx->tok_tower_siz) {
		n = uls_roundup(istr_ctx->tok_tower_len + 1, 32);
		istr_ctx->tok_tower = (uls_token_t *) uls_mrealloc(
			istr_ctx->tok_tower, n*sizeof(uls_token_t));
		istr_ctx->tok_tower_siz = n;
	}

	bak_lit = istr_ctx->tok_tower + istr_ctx->tok_tower_len;

	bak_lit->id = sym_no;

	if (dat_len < 0) { // dat is a void-ptr.
		bak_lit->text = dat;
		bak_lit->offset = -1;
		bak_lit->len_text = dat_len;

	} else { // dat_len >= 0, dat is a text.
		bak_lit->text = NULL;
		bak_lit->offset = k = istr_ctx->cstr_pool_len;
		bak_lit->len_text = dat_len;

		k = str_append(&istr_ctx->cstr_pool, &istr_ctx->cstr_pool_siz, k, (uls_char_t *) dat, dat_len);
		str_putc(&istr_ctx->cstr_pool, &istr_ctx->cstr_pool_siz, k, '\0');

		istr_ctx->cstr_pool_len = k + 1;
	}

	++istr_ctx->tok_tower_len;
}

// <brief>
// Prints the information of the current token.
// </brief>
// <return>none</return>
void
uls_prntok(uls_lex_t* uls)
{
	int    i, rc, token_id = uls->tok;
	const char*keystr;
	uls_char_t *ptr;
	char   prefix[4], ch_grp;
	uls_uint32 uch;

	ptr = uls_lexeme_text(uls);

	if (token_id == uls->toknum_ID) {
		uls_printf("\t[     ID]  ");
		for ( ; *ptr!='\0'; ptr+=rc) {
			if ((rc=uls_decode_utf8(ptr, &uch)) <= 1) {
				uls_printf("%c", *ptr);
				rc = 1;
			} else {
				uls_printf("\\u%04X", uch);
			}
		}
		uls_printf("\n");

	} else if (token_id == uls->toknum_NUMBER) {
		i = 0;

		if (*ptr == '-') {
			prefix[i++] = '-';
			++ptr;
		}

		if (*ptr == '.') {
			prefix[i++] = '0';
		} else {
			prefix[i++] = '0';
			prefix[i++] = 'x';
		}
		prefix[i] = '\0';
		uls_printf("\t[ NUMBER]  %s%s\n", prefix, ptr);

	} else if (token_id == uls->toknum_EOI) {
		uls_printf("\t[toknum_EOI] %s\n", ptr);

	} else if (token_id == uls->toknum_EOF) {
		uls_printf("[    EOF]  %s", ptr);

	} else if (token_id == '\n') {
		uls_printf("\t[     LF]\n");

	} else if (token_id == '\t') {
		uls_printf("\t[    TAB]\n");

	} else if ((keystr=(const char *) get_keyword_str(uls, token_id)) != NULL) {
		uls_printf("\t[%7s]  %s\n", keystr, ptr);

	} else if (token_id>=0 && token_id<ULS_SYNTAX_TABLE_SIZE && isprint(token_id)) {
		ch_grp = uls->i_str.ch_context[token_id];

		if (ch_grp & ULS_CH_QUOTE) {
			uls_printf("\t[      %c]  %c%s%c\n",
				token_id, token_id, ptr, token_id);
		} else {
			uls_printf("\t[      %c]\n", token_id);
		}

	} else {
		uls_printf("\t[    %3d] ???\n", token_id);
	}
}

// <brief>
// This encodes the packet info into <*p_outbuf+k,p_outsiz>
// </brief>
// <parm name="uls">The lexical object</parm>
// <parm name="p_outbuf">The pointer of output buffer</parm>
// <parm name="p_outsiz">The allocated size of *p_outbuf</parm>
// <parm name="k">The start position in *p_outbuf</parm>
// <parm name="mode">ULS_STREAM_BIN_LE | ULS_STREAM_BIN_BE</parm>
// <return>The length of *p_outbuf</return>
static int
append_packet_to_outbuf(uls_lex_t* uls, uls_char_t** p_outbuf, int* p_outsiz, int k, int mode)
{
	uls_int32 hdrbuf[ULS_BIN_RECHDR_NUM_INT32];
	int txtlen, reclen;

	txtlen = uls_lexeme_textlen(uls);
	reclen = ULS_BIN_RECHDR_SZ + txtlen + 1; // +1 == '\0'
	str_append(p_outbuf, p_outsiz, k, NULL, reclen);

	hdrbuf[0] = uls->tok;
	hdrbuf[1] = txtlen;

#ifdef ULS_BIG_ENDIAN
	if (mode == ULS_STREAM_BIN_LE) {
		uls_reverse_array((uls_byte_t *) hdrbuf, sizeof(uls_int32));
		uls_reverse_array((uls_byte_t *) hdrbuf + sizeof(uls_int32), sizeof(uls_int32));
	}
#else
	if (mode == ULS_STREAM_BIN_BE) {
		uls_reverse_array((uls_byte_t *) hdrbuf, sizeof(uls_int32));
		uls_reverse_array((uls_byte_t *) hdrbuf + sizeof(uls_int32), sizeof(uls_int32));
	}
#endif
	k = str_append(p_outbuf, p_outsiz, k, (uls_char_t *) hdrbuf, ULS_BIN_RECHDR_SZ);

	k = str_append(p_outbuf, p_outsiz, k, uls_lexeme_text(uls), txtlen);
	str_putc(p_outbuf, p_outsiz, k++, '\0');

	k = uls_roundup(k, 4); // aligned by 4
	str_append(p_outbuf, p_outsiz, k, NULL, 0);

	return k;
}

// <brief>
// This starts streaming into 'hdr', uls-file.
// </brief>
// <parm name="hdr">The structure having uls-file information</parm>
// <return>0 if it succeeds, -1 otherwise</return>
static int
__do_uls_start_stream(uls_stream_t *hdr)
{
	uls_lex_t* uls = hdr->uls;
	int  fd = hdr->out_fd;
	int  k, stat=0;

	const char *tagstr;
	char tag_buf[ULS_FILEPATH_MAX+1], lno_buf[32];
	int  len_lno_buf, len_tag_buf, lno;

	int  reclen, txtlen, is_same_lno, is_same_tag;
	uls_int32 hdrbuf[ULS_BIN_RECHDR_NUM_INT32];
	int mode, numbering;

	mode = hdr->out_fd_mode;
	numbering = hdr->flags & ULS_FL_LINE_NUMBERING;

	tagstr = uls_get_tag(uls);

	if (strlen(tagstr) == 0) {
		tag_buf[0] = '?';
		tag_buf[1] = '\0';
		len_tag_buf = 1;
	} else {
		tag_buf[0] = '\0';
		len_tag_buf = 0;
	}

	lno = -1;
	k = 0;

	while (1) {
		uls_gettok(uls);

		if (k > 1024 || uls->tok == uls->toknum_EOI) {
			if (uls_writen(fd, hdr->outbuf, k) < k) {
				err_log("I/O error");
				stat = -1;
				break;
			}

			k = 0;

			if (uls->tok == uls->toknum_EOI) {
				break;
			}
		}

		if (uls->istr_tower != NULL) {
			is_same_lno = (lno == uls_get_lineno(uls)) ? 1 : 0;
			is_same_tag = uls_streql(tag_buf, uls_get_tag(uls)) ? 1 : 0;
	
			if (is_same_lno == 0 || is_same_tag == 0) {
				lno = uls_get_lineno(uls);

				tagstr = uls_get_tag(uls);
				strncpy(tag_buf, tagstr, ULS_FILEPATH_MAX);
				tag_buf[ULS_FILEPATH_MAX] = '\0';
				len_tag_buf = strlen(tag_buf);

				if (numbering) {
					len_lno_buf = uls_snprintf(lno_buf, sizeof(lno_buf), "%X", lno);
					if (is_same_tag) {
						txtlen = len_lno_buf;
					} else {
						txtlen = len_lno_buf + 1 + len_tag_buf; // +1 == the separator ' '
					}

					reclen = ULS_BIN_RECHDR_SZ + txtlen + 1; // +1 == '\0'
					str_append(&hdr->outbuf, &hdr->outbuf_siz, k, NULL, reclen);

					hdrbuf[0] = uls->toknum_LINENUM;
					hdrbuf[1] = txtlen;
		
#ifdef ULS_BIG_ENDIAN
					if (mode == ULS_STREAM_BIN_LE) {
						uls_reverse_array((uls_byte_t *) hdrbuf, sizeof(uls_int32));
						uls_reverse_array((uls_byte_t *) hdrbuf + sizeof(uls_int32), sizeof(uls_int32));
					}
#else
					if (mode == ULS_STREAM_BIN_BE) {
						uls_reverse_array((uls_byte_t *) hdrbuf, sizeof(uls_int32));
						uls_reverse_array((uls_byte_t *) hdrbuf + sizeof(uls_int32), sizeof(uls_int32));
					}
#endif
					k = str_append(&hdr->outbuf, &hdr->outbuf_siz, k, (uls_char_t *) hdrbuf, ULS_BIN_RECHDR_SZ);

					k = str_append(&hdr->outbuf, &hdr->outbuf_siz, k, (uls_char_t *) lno_buf, len_lno_buf);
					if (is_same_tag == 0) {
						str_putc(&hdr->outbuf, &hdr->outbuf_siz, k++, ' '); // ' ' == the separator
						k = str_append(&hdr->outbuf, &hdr->outbuf_siz, k, (uls_char_t *) tag_buf, len_tag_buf);
					}

					str_putc(&hdr->outbuf, &hdr->outbuf_siz, k++, '\0');

					k = uls_roundup(k, 4); // aligned by 4
					str_append(&hdr->outbuf, &hdr->outbuf_siz, k, NULL, 0);
				}
			}
		}

		k = append_packet_to_outbuf(uls, &hdr->outbuf, &hdr->outbuf_siz, k, mode);
	}

	return stat;
}

// <brief>
// This starts streaming into 'hdr', uls-file.
// </brief>
// <parm name="hdr">The uls-header for output file.</parm>
// <parm name="in_hdr">The uls-header of input.</parm>
// <parm name="flags">ULS_FL_LINE_NUMBERING | ULS_WANT_EOF_TOK</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_start_stream(uls_stream_t *hdr, uls_stream_t* in_hdr, int flags)
{
	uls_lex_t* uls = hdr->uls;

	if (in_hdr->filetype != ULS_STREAM_RAW) {
		if (!uls_streql(uls->spec_name, in_hdr->specname) ||
			uls_version_cmp_code(&uls->stream_filever, &in_hdr->filever) < 2) {
			err_log("%s: Unsupported version: ", in_hdr->tag);
			return -1;
		}
	}

	if (uls_push_file_hdr(uls, in_hdr, flags) < 0) {
		err_log("%s: fail to prepare input-stream", __FUNCTION__);
		return -1;
	}

	return __do_uls_start_stream(hdr);
}

// <brief>
// This creates the uls-header for streaming.
// </brief>
// <parm name="hdr">The uls-header for output file</parm>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">The file descriptor for output file</parm>
// <parm name="str_typ">(ULS_STREAM_BIN_{LE|BE} + (ULS_FL_LINE_NUMBERING | ULS_WANT_EOF_TOK)</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_create_stream(uls_stream_t *hdr, uls_lex_t* uls, int fd, int str_typ, char* subname)
{
	char curr_uls_ver_str[ULS_VERSION_STR_MAXLEN+1];
	char *mode_str, time_buf[ULS_STREAM_CTIME_SIZE+1];
	char ulshdr[256];
	int  i, mode;

	if (fd < 0 || uls == NULL) {
		err_log("out-fd for streaming is negative!");
		return -1;
	}

	uls_init_stream(hdr, uls, fd);
	hdr->out_fd_mode = mode = str_typ & 0x0F;
	hdr->flags |= ULS_STREAM_WRITING;
	if (str_typ & ULS_FL_LINE_NUMBERING) {
		hdr->flags |= ULS_FL_LINE_NUMBERING;
	}

	if (mode == ULS_STREAM_BIN_LE) { // LITTLE-ENDIAN
		mode_str = "BIN/LITTLE";
	} else if (mode == ULS_STREAM_BIN_BE) { // BIG-ENDIAN
		mode_str = "BIN/BIG";
	} else {
		return -2;
	}

	uls_version_make_string(&uls->stream_filever, curr_uls_ver_str);

	uls_strcpy(ulshdr, "#34183847-D64D-C131-D754-577215664901-ULS-STREAM\n");
	i = strlen(ulshdr);
	i += uls_snprintf(ulshdr+i, sizeof(ulshdr)-i, "FILE_VERSION: %s\n", curr_uls_ver_str);
	i += uls_snprintf(ulshdr+i, sizeof(ulshdr)-i, "NAME: %s\n", uls->spec_name);
	i += uls_snprintf(ulshdr+i, sizeof(ulshdr)-i, "TYPE: %s\n", mode_str);

	get_current_time_yyyymmdd_hhmm(time_buf, sizeof(time_buf));
	i += uls_snprintf(ulshdr+i, sizeof(ulshdr)-i, "CREATION_TIME: %s\n", time_buf);

	if (subname == NULL) subname = "";
	i += uls_snprintf(ulshdr+i, sizeof(ulshdr)-i, "TAG: %s\n", subname);

	//
	// The header section of the file must be termininated by an empty line.
	//
	ulshdr[i++] = '\n';

	if (uls_writen(hdr->out_fd, (uls_char_t *) ulshdr, i) < i) {
		err_log("I/O error");
		return -1;
	}

	return 0;
}

// <brief>
// This closes the uls-header that is open for input or output.
// </brief>
// <parm name="hdr">The open uls-header </parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_close_stream(uls_stream_t *hdr)
{
	uls_lex_t* uls = hdr->uls;
	int  k=0, stat=0;

	if (hdr->flags & ULS_STREAM_WRITING) {
		uls->tok = uls->toknum_EOI;
		append_open_comment_to_lexeme(uls, 0, uls->i_str.fd_context.last_end_mark);
		k = append_packet_to_outbuf(uls, &hdr->outbuf, &hdr->outbuf_siz, k, hdr->out_fd_mode);
	
		if (uls_writen(hdr->out_fd, hdr->outbuf, k) < k) {
			err_log("I/O error");
			stat = -1;
		}

		hdr->out_fd = -1;
		hdr->out_fd_mode = 0;
	}

	if (hdr->flags & ULS_STREAM_FDCLOSE) {
		uls_close(hdr->fd);
		hdr->flags &= ~ULS_STREAM_FDCLOSE;
	}

	hdr->fd = -1;
	uls_deinit_stream(hdr);
	return stat;
}

#ifndef WIN32
// <brief>
// This sets a uls-header with 'fd' filtered by 'fdf'
// </brief>
// <parm name="fd">The file descriptor to be filtered by 'fdf'</parm>
// <parm name="fdf">A filter object</parm>
// <parm name="hdr">The uls-header set with the file descriptor filtered by 'fdf'</parm>
// <return>The file descriptor reprocessed</return>
int
uls_open_stream_filter_fd(int fd, fdf_t* fdf, uls_stream_t* hdr)
{
	if (fd < 0) return -1;

	fd = fdf_open(fdf, fd);

	if (uls_open_stream_fd(fd, hdr) < 0) {
		fdf_close(fdf);
		return -1;
	}

	// set hdr->filepath after calling this func
	return fd;
}

// <brief>
// This closes the uls-header opened by 'uls_open_stream_filter_fd'
// </brief>
// <parm name="hdr">A uls-header to be closed</parm>
// <parm name="fdf">The filter object associated with 'hdr'</parm>
// <return>none</return>
void
uls_close_stream_filter(uls_stream_t *hdr, fdf_t* fdf)
{
	uls_close_stream(hdr);

	if (fdf_close(fdf) < 0)
		err_log("%s: fail to reap filter procs\n", __FUNCTION__);
}
#endif

// <brief>
// This initializes the uls-header structure with 'uls' and 'fd'
// </brief>
// <parm name="hdr">The open uls-header to be initialized</parm>
// <parm name="uls">The lexical object</parm>
// <parm name="fd">The file descriptor of input or output</parm>
// <return>none</return>
void
uls_init_stream(uls_stream_t *hdr, uls_lex_t* uls, int fd)
{
	hdr->flags = 0;
	hdr->fd = fd;
	hdr->specname[0] = '\0'; // unknown
	uls_version_make(&hdr->filever, 0, 0, 0, 0);  // unknown
	hdr->tag[0] = '\0';
	hdr->firstline[0] = '\0';
	hdr->len_firstline = 0;
	hdr->subname[0] = '\0';
	hdr->specname[0] = '\0'; // unknown
	hdr->uls = uls;
	hdr->out_fd = fd;
	str_init(&hdr->outbuf, &hdr->outbuf_siz, 1024);
}

// <brief>
// The counterpart of uls_init_stream().
// Deallocates the memories in 'hdr'
// </brief>
// <parm name="hdr">The open uls-header to be deinitialized</parm>
// <return>none</return>
void
uls_deinit_stream(uls_stream_t *hdr)
{
	str_free(&hdr->outbuf, &hdr->outbuf_siz);
	hdr->out_fd = -1;
}

// <brief>
// Opens the uls-file indicated by fd.
// Stores the additional information of the uls-file to 'hdr'
// </brief>
// <parm name="fd">The file descriptor of input</parm>
// <parm name="hdr">The open uls-header</parm>
// <return>0 if it succeeds, -1 otherwise</return>
int
uls_open_stream_fd(int fd, uls_stream_t *hdr)
{
	// ret-val < 0: system error
	// ret-val ==0: success
	const char* magic_code = "#34183847-D64D-C131-D754-577215664901-ULS-STREAM\n";
	int  magic_code_len = uls_strlen(magic_code);
	char linebuff[ULS_MAGICCODE_SIZE+1], *wrd[2];
	int  len, n_parms, ftype, stat=0;

	if (fd < 0) return -1;
	uls_init_stream(hdr, NULL, fd);

	linebuff[magic_code_len] = '\0';
	if ((len=uls_read(fd, linebuff, magic_code_len)) < 0) {
		err_log("I/O Error");
		return -1;
	} else if (len < magic_code_len || !uls_streql(linebuff, magic_code)) {
		hdr->filetype = ULS_STREAM_RAW;
		memcpy(hdr->firstline, linebuff, len);
		hdr->firstline[len] = '\0';
		hdr->len_firstline = len;
		return 0;
	}

	while ((len=uls_readline(fd, linebuff, sizeof(linebuff))) > 0) {
		if (len > 0 && linebuff[len-1] == '\n')
			linebuff[--len] = '\0';

		if (len == 0) break; // normally exit'd

		n_parms = uls_get_column_words(linebuff, ' ', wrd, 2);

		if (uls_streql(wrd[0], "FILE_VERSION:")) {
			if (uls_version_pars_str(wrd[1], &hdr->filever) < 0) {
				err_log("Unsupported version: %s", wrd[1]);
				stat = -2;
				break;
			}

		} else if (uls_streql(wrd[0], "NAME:")) {
			strncpy(hdr->specname, wrd[1], ULS_LEXSTR_MAXSIZ);
			hdr->specname[ULS_LEXSTR_MAXSIZ] = '\0';

		} else if (uls_streql(wrd[0], "TYPE:")) {
			if (uls_streql(wrd[1], "BIN/LITTLE")) {
				ftype = ULS_STREAM_BIN_LE;
			} else if (uls_streql(wrd[1], "BIN/BIG")) {
				ftype = ULS_STREAM_BIN_BE;
			} else {
				err_log("Unsupported file-type: %s", wrd[1]);
				stat = -3;
				break;
			}
			hdr->filetype = ftype;

		} else if (uls_streql(wrd[0], "CREATION_TIME:")) {
			strncpy(hdr->ctime, wrd[1], ULS_STREAM_CTIME_SIZE);
			hdr->ctime[ULS_STREAM_CTIME_SIZE] = '\0';

		} else if (uls_streql(wrd[0], "TAG:")) {
			strncpy(hdr->subname, wrd[1], ULS_STREAM_SUBNAME_SIZE);
			hdr->subname[ULS_STREAM_SUBNAME_SIZE] = '\0';

		} else {
			err_log("unknown attribute in uls-header: %s\n", wrd[0]);
		}
	}

	return stat;
}

// <brief>
// Gets a sequence of tokens from uls-file.
// Stores the additional information of the uls-file to 'hdr'
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>0 if it succeeds, 1 if not arranged</return>
int
uls_gettok_bin(uls_lex_t* uls)
{
	uls_istream_t *istr = &uls->i_str;
	uls_char_t  *lptr;
	int   tok_id, rc, txtlen;
	uls_int32  *hdrbuf;

 next_loop:
	lptr = istr->fd_context.lptr;
	if (lptr >= istr->fd_context.line_end) {
		if ((rc=uls_fillbuff(uls)) < 0) {
			err_log("%s: IO-error", __FUNCTION__);
			uls->tok = uls->toknum_ERR;
			uls->tokbuf[0] = '\0';
			uls->s_val = uls->tokbuf;
			uls->s_val_len = 0;
			return 0;
		} else if (rc > 0) {
			// actually, rc == 1
			goto next_loop; // normal case
		}

		return 1;
	}

	hdrbuf = (uls_int32 *) lptr;
	lptr += ULS_BIN_RECHDR_SZ;

	tok_id = hdrbuf[0];
	txtlen = hdrbuf[1];

	str_append(&uls->tokbuf, &uls->tokbuf_siz, 0, lptr, txtlen);
	str_putc(&uls->tokbuf, &uls->tokbuf_siz, txtlen, '\0');
	lptr += uls_roundup(txtlen+1, 4); // +1 == '\0'

	istr->fd_context.lptr = lptr;

	if (tok_id == uls->toknum_LINENUM) {
		lptr = uls_lexeme_text(uls);
		rc = skip_atox((char **) &lptr);

		if (*lptr == '\0') lptr = NULL;
		else ++lptr; // skip the ' '

		uls_istr_set_tag(istr, (char *) lptr, rc);
		goto next_loop;
	}

	if (tok_id == uls->toknum_EOI) {
		return 1;
	}

	if (tok_id == uls->toknum_EOF &&
		(istr->fd_context.flags & ULS_WANT_EOF_TOK) == 0) {
			goto next_loop;
	}

	uls->tok = tok_id;
	uls->s_val = uls->tokbuf;
	uls->s_val_len = txtlen;

	return 0;
}

// <brief>
// Checks whether the string pointed by 'ptr is a real number or not.
// </brief>
// <parm name="ptr">A number string</parm>
// <return>1 if it's real, 0 otherwise</return>
static int
__uls_is_real(const char *ptr)
{
	if (*ptr == '-') ++ptr;
	if (*ptr == '0') ++ptr;
	if (*ptr == '.') return 1;

	return 0;
}

// <brief>
// Checks whether the current lexeme is a real number or not.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>1 if it's real, 0 otherwise</return>
int uls_is_real(uls_lex_t* uls)
{
	if (uls->tok != uls->toknum_NUMBER) {
		return 0;
	}

	return __uls_is_real((const char *) uls_lexeme_text(uls));
}

// <brief>
// Checks whether the current lexeme is an integer or not.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>1 if it's real, 0 otherwise</return>
int uls_is_integer(uls_lex_t* uls)
{
	if (uls->tok != uls->toknum_NUMBER) {
		return 0;
	}

	return !__uls_is_real((const char *) uls_lexeme_text(uls));
}

// <brief>
// If the current lexeme is an integer and proper for 'unsigned long',
// this procedure converts the lexeme into 'unsigned long' and returns it.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>the integer value</return>
unsigned long
uls_lexeme_ulong(uls_lex_t* uls)
{
	const char *ptr;
	unsigned long l_val;
	int minus=0;

	if (uls->tok != uls->toknum_NUMBER) {
		err_log("The current token is NOT!");
		return 0;
	}

	ptr = (const char *) uls_lexeme_text(uls);
	if (*ptr == '-') {
		minus = 1;
		++ptr;
	}

	if (*ptr == '.') {
		l_val = (unsigned long) atof(ptr);
	} else {
		l_val = strtoul(ptr, NULL, 16);
	}

	if (minus) l_val = (unsigned long) (-(long) l_val);
	return l_val;
}

// <brief>
// If the current lexeme is an integer and proper for 'int',
// this procedure will convert it into 'int' and return it.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>the integer value</return>
int
uls_lexeme_int(uls_lex_t* uls)
{
	const char *ptr;
	int i_val, minus = 0;

	if (uls->tok != uls->toknum_NUMBER) {
		err_log("The current token is NOT number!");
		return 0;
	}

	ptr = (const char *) uls_lexeme_text(uls);
	if (*ptr == '-') {
		minus = 1;
		++ptr;
	}

	if (*ptr == '.') {
		i_val = (int) atof(ptr);
	} else {
		i_val = (int) strtol(ptr, NULL, 16);
	}

	if (minus) i_val = -i_val;
	return i_val;
}

// <brief>
// If the current lexeme is an floting number and proper for 'double',
// this procedure will convert it into 'double' and return it.
// </brief>
// <parm name="uls">The lexical object</parm>
// <return>the floating number</return>
double
uls_lexeme_double(uls_lex_t* uls)
{
	const char *ptr;
	double d_val;
	int minus=0;

	if (uls->tok != uls->toknum_NUMBER) {
		err_log("The current token is NOT number!");
		return 0.;
	}

	ptr = (const char *) uls_lexeme_text(uls);
	if (*ptr == '-') {
		minus = 1;
		++ptr;
	}

	if (*ptr == '.') {
		d_val = atof(ptr);
	} else {
		d_val = (double) strtol(ptr, NULL, 16);
	}

	if (minus) d_val = -d_val;
	return d_val;
}
